package com.dimples.dd.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.constant.DDConstant;
import com.dimples.dd.common.model.Option;
import com.dimples.dd.common.pojo.PageResult;
import com.dimples.dd.security.core.util.SecurityUtils;
import com.dimples.dd.system.convert.RoleConverter;
import com.dimples.dd.system.core.dal.redis.RedisKeyConstants;
import com.dimples.dd.system.enums.permission.RoleCodeEnum;
import com.dimples.dd.system.enums.permission.RoleTypeEnum;
import com.dimples.dd.system.mapper.SystemRoleMapper;
import com.dimples.dd.system.model.entity.SystemRoleDO;
import com.dimples.dd.system.model.form.RoleForm;
import com.dimples.dd.system.model.query.RolePageQuery;
import com.dimples.dd.system.model.vo.RolePageVO;
import com.dimples.dd.system.service.PermissionService;
import com.dimples.dd.system.service.SystemRoleService;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.dimples.dd.common.exception.BizExceptionUtil.exception;
import static com.dimples.dd.common.exception.ErrorCode.*;
import static com.dimples.dd.system.enums.LogRecordConstants.*;

@Service
@RequiredArgsConstructor
public class SystemRoleServiceImpl extends ServiceImpl<SystemRoleMapper, SystemRoleDO> implements SystemRoleService {

    private final RoleConverter roleConverter;

    @Autowired
    private PermissionService permissionService;

    @Override
    public List<Option<String>> listRoleOptions() {
        // 查询数据
        List<SystemRoleDO> roleList = this
                .list(new LambdaQueryWrapper<SystemRoleDO>()
                        .select(SystemRoleDO::getCode, SystemRoleDO::getName)
                        .ne(!SecurityUtils.isRoot(), SystemRoleDO::getCode, DDConstant.ROOT_ROLE_CODE)
                        .orderByAsc(SystemRoleDO::getSort)
                );

        // 实体转换
        return roleConverter.entities2Options(roleList);
    }

    @Override
    public PageResult<RolePageVO> getRolePage(RolePageQuery queryParams) {

        // 查询数据
        PageResult<SystemRoleDO> rolePage = this.baseMapper.getRolePage(queryParams);

        return roleConverter.entity2Page(rolePage);
    }

    @Override
    @DSTransactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}",
            success = SYSTEM_ROLE_CREATE_SUCCESS)
    public boolean createRole(RoleForm roleForm) {
        // 1. 校验角色
        validateRoleDuplicate(roleForm.getName(), roleForm.getCode(), null);

        // 2. 插入到数据库
        SystemRoleDO role = roleConverter.form2Entity(roleForm);
        role.setType(RoleTypeEnum.CUSTOM.getType().byteValue());
        this.baseMapper.insert(role);

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("role", role);
        return true;
    }

    @Override
    @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = SYSTEM_ROLE_UPDATE_SUCCESS)
    public boolean updateRole(RoleForm roleForm) {
        // 1.1 校验是否可以更新
        List<SystemRoleDO> oldRole = validateRoleForUpdate(CollUtil.newArrayList(roleForm.getId()));
        // 1.2 校验角色的唯一字段是否重复
        validateRoleDuplicate(roleForm.getName(), roleForm.getCode(), roleForm.getId());

        // 2. 更新到数据库
        SystemRoleDO updateObj = roleConverter.form2Entity(roleForm);
        int update = this.baseMapper.updateById(updateObj);

        if (update > 0) {
            // 判断角色编码或状态是否修改，修改了则刷新权限缓存
            if (CollUtil.isNotEmpty(oldRole)
                    && (
                    !StrUtil.equals(oldRole.get(0).getCode(), roleForm.getCode()) ||
                            !ObjectUtil.equals(oldRole.get(0).getStatus(), roleForm.getStatus())
            )) {
                this.permissionService.refreshRolePermsCache(oldRole.get(0).getCode(), roleForm.getCode());
            }
        }

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("role", updateObj);
        return true;
    }

    /**
     * 校验角色是否可以被更新
     *
     * @param id 角色编号
     */
    @VisibleForTesting
    List<SystemRoleDO> validateRoleForUpdate(List<Long> id) {
        List<SystemRoleDO> role = this.baseMapper.selectBatchIds(id);
        if (CollUtil.isEmpty(role)) {
            throw exception(ROLE_NOT_EXISTS);
        }
        role.forEach(item -> {
            // 内置角色，不允许删除
            if (RoleTypeEnum.SYSTEM.getType().equals(item.getType().intValue())) {
                throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
            }
        });
        return role;
    }

    /**
     * 校验角色的唯一字段是否重复
     * <p>
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     *
     * @param name 角色名字
     * @param code 角色额编码
     * @param id   角色编号
     */
    @VisibleForTesting
    void validateRoleDuplicate(String name, String code, Long id) {
        // 0. 超级管理员，不允许创建
        if (RoleCodeEnum.isSuperAdmin(code)) {
            throw exception(ROLE_ADMIN_CODE_ERROR, code);
        }
        // 1. 该 name 名字被其它角色所使用
        SystemRoleDO role = this.baseMapper.selectOne(SystemRoleDO::getName, name);
        if (role != null && !role.getId().equals(id)) {
            throw exception(ROLE_NAME_DUPLICATE, name);
        }
        // 2. 是否存在相同编码的角色
        if (!StringUtils.hasText(code)) {
            return;
        }
        // 该 code 编码被其它角色所使用
        role = this.baseMapper.selectOne(SystemRoleDO::getCode, code);
        if (role != null && !role.getId().equals(id)) {
            throw exception(ROLE_CODE_DUPLICATE, code);
        }
    }

    @Override
    public RoleForm getRoleForm(Long roleId) {
        SystemRoleDO entity = this.getById(roleId);
        return roleConverter.entity2Form(entity);
    }

    @Override
    public boolean updateRoleStatus(Long roleId, Integer status) {
        validateRoleForUpdate(CollUtil.newArrayList(roleId));
        SystemRoleDO role = new SystemRoleDO();
        role.setStatus(status.byteValue());
        boolean result = this.updateById(role);
        if (result) {
            // 刷新角色的权限缓存
            this.permissionService.refreshRolePermsCache(role.getCode());
        }

        return result;
    }

    @Override
    public List<Long> listIdsByCodes(List<String> roles) {
        return this.baseMapper.listIdsByCodes(roles);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
            success = SYSTEM_ROLE_DELETE_SUCCESS)
    public boolean deleteRoles(String ids) {
        List<Long> roleIds = Arrays.stream(ids.split(StrUtil.COMMA)).map(Long::parseLong).toList();
        // 1. 校验是否可以删除
        List<Long> roleIdsExistAssignedUsers = this.permissionService.selectRoleIdsExistAssignedUsers(roleIds);
        if (CollUtil.isNotEmpty(roleIdsExistAssignedUsers)) {
            List<SystemRoleDO> roles = this.baseMapper.selectBatchIds(roleIdsExistAssignedUsers);
            throw exception(ROLE_IS_BOUND, StrUtil.join(StrUtil.COMMA, roles.stream().map(SystemRoleDO::getName).collect(Collectors.toList())));
        }
        List<SystemRoleDO> roles = this.baseMapper.selectBatchIds(roleIds);
        if (CollUtil.isEmpty(roles)) {
            throw exception(ROLE_NOT_EXISTS);
        }
        //2. 删除
        boolean deleteResult = this.removeBatchByIds(roleIds);
        if (deleteResult) {
            // 2.1 删除成功，刷新权限缓存
            for (SystemRoleDO roleDO : roles) {
                this.permissionService.refreshRolePermsCache(roleDO.getCode());
            }
        }

        // 3. 记录操作日志上下文
        SystemRoleDO systemRoleDO = new SystemRoleDO();
        systemRoleDO.setName(StrUtil.join(StrUtil.COMMA, roles.stream().map(SystemRoleDO::getName).collect(Collectors.toList())));
        LogRecordContext.putVariable("role", systemRoleDO);
        return true;
    }

    @Override
    public List<SystemRoleDO> getRoleList(Collection<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return this.baseMapper.selectBatchIds(ids);
    }

    @Override
    @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", unless = "#result == null")
    public SystemRoleDO getRoleFromCache(Long id) {
        return this.baseMapper.selectById(id);
    }

    @Override
    public boolean hasAnySuperAdmin(Set<Long> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return false;
        }

        return roleIds.stream().anyMatch(id -> {
            SystemRoleDO role = this.getRoleFromCache(id);
            return role != null && RoleCodeEnum.isSuperAdmin(role.getCode());
        });
    }
}

























